﻿using UnityEngine;
using UnityEngine.Networking;
using UnityEditor;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using Common.Utils;
using Common.Utils.Helper;


public class BuildAssetBundles  {
    private const string GameVersion = "0.0.0";
    private const string VersionType = "dev";  // dev、alpha、beta、RC、release

    private const string strRootAssetName = "AssetBundles";
    private const string strVersionName = "VersionInfo";

    private static readonly string strSrcPath = Application.dataPath;
    private static readonly string strGenPath = "PlatformResources";

    private static readonly string[] strSrciptsPaths =
    {
        "Configs",
        "Scripts",
        "Protos"
    };

    private static readonly string[] strPackPaths =
    {
        "Configs",
        "Scripts",
        "Protos",
        "Prefabs",
        "Textures",
        "Materials"
    };


    [MenuItem("AssetsBundle/Generate Product")]
    static void GenerateProductAssetsBundles()
    {
        BuildTarget target = _GetProductBuildTarget(Application.platform);
        _GeneratePlatformAssetBundles(target
            , BuildAssetBundleOptions.None 
            //| BuildAssetBundleOptions.AppendHashToAssetBundleName
            );
    }

    [MenuItem("AssetsBundle/Generate Android")]
    static void GenerateAndroidAssetsBundles()
    {
        _GeneratePlatformAssetBundles(BuildTarget.Android
            , BuildAssetBundleOptions.None
            );
    }

    private static void _GeneratePlatformAssetBundles(BuildTarget _platform, BuildAssetBundleOptions _babo = BuildAssetBundleOptions.None)      //进行打包
    {
        // 1. 删除原目录
        _DeleteProductPath(Application.platform);
        _DeleteGeneratePath(_platform);

        // 2. 处理脚本
        PreProccessLuaScript();

        // 3. 设置标签
        SetAssetsBundlesLabel();

        // 4. 生成资源
        string genPath = _GetGeneratePath(_platform);
        // 判断该目录是否存在
        if (Directory.Exists(genPath) == false)
        {
            Directory.CreateDirectory(genPath);       //在工程下创建AssetBundles目录
        }
        // 参数一为打包到哪个路径，参数二压缩选项  参数三 平台的目标
        BuildPipeline.BuildAssetBundles(genPath, _babo, _platform);

        _GenerateVersionInfo(_platform);

        // 5. 拷贝到生产目录
        string proPath = _GetProductPath(Application.platform);
        _CopyDirectory(genPath, proPath);

        // 6. 删除临时目录
        _ClearAllScriptsPath();
    }


    private static string _GetProductPath(RuntimePlatform platform)
    {
        string proPath = string.Empty;
        switch(platform)
        {
            case RuntimePlatform.OSXEditor:
            case RuntimePlatform.WindowsEditor:
                proPath = Application.streamingAssetsPath;
                break;
            default:
                break;
        }
        return proPath;
    }


    private static BuildTarget _GetProductBuildTarget(RuntimePlatform platform)
    {
        BuildTarget target = BuildTarget.NoTarget;
        switch(platform)
        {
            case RuntimePlatform.OSXEditor:
                target = BuildTarget.StandaloneOSX;
                break;
            case RuntimePlatform.WindowsEditor:
                target = BuildTarget.StandaloneWindows;
                break;
            default:
                break;
        }
        return target;
    }


    private static void _DeleteProductPath(RuntimePlatform platform)
    {
        string proPath = _GetProductPath(platform);
        if (string.IsNullOrEmpty(proPath))
            return;

        if (!Directory.Exists(proPath))
            return;

        Directory.Delete(proPath, true);
        File.Delete(proPath + ".meta");
        Directory.CreateDirectory(proPath);
        AssetDatabase.Refresh();
    }


    private static void _DeleteGeneratePath(BuildTarget platform)
    {
        string genPath = _GetGeneratePath(platform);
        if (string.IsNullOrEmpty(genPath))
            return;

        if (!Directory.Exists(genPath))
            return;

        Directory.Delete(genPath, true);
        File.Delete(genPath + ".meta");
        AssetDatabase.Refresh();
    }


    private static void _ClearAllScriptsPath()
    {
        foreach (string strPath in strSrciptsPaths)
        {
            string strScriptPath = string.Concat(strSrcPath, "/", strPath);
            _ClearScriptsPath(strScriptPath);
        }
    }

    private static void _ClearScriptsPath(string _strScriptPath)
    {
        string strSrciptsPath = _strScriptPath;
        if (string.IsNullOrEmpty(strSrciptsPath))
            return;

        if (!Directory.Exists(strSrciptsPath))
            return;

        DirectoryInfo dir = new DirectoryInfo(strSrciptsPath);

        foreach (FileInfo file in dir.GetFiles("*.txt", SearchOption.AllDirectories))
        {
            file.Delete();
        }

        File.Delete(strSrciptsPath + ".meta");
        AssetDatabase.Refresh();
    }


    private static string _GetGeneratePath(BuildTarget platform)
    {
        string genPath = string.Empty;
        switch (platform)
        {
            case BuildTarget.iOS:
                genPath = strGenPath + "/IOS/" + strRootAssetName;
                break;
            case BuildTarget.Android:
                genPath = strGenPath + "/Android/" + strRootAssetName;
                break;
            case BuildTarget.StandaloneOSX:
                genPath = strGenPath + "/OSX/" + strRootAssetName;
                break;
            case BuildTarget.StandaloneWindows:
                genPath = strGenPath + "/Windows/" + strRootAssetName;
                break;
            case BuildTarget.StandaloneWindows64:
                genPath = strGenPath + "/Windows64/" + strRootAssetName;
                break;
            default:
                break;
        }
        return genPath;
    }


    private static void _CopyDirectory(string _srcPath, string _destPath)
    {
        try
        {
            //如果不存在目标路径，则创建之
            if (!Directory.Exists(_destPath))
            {
                Directory.CreateDirectory(_destPath);
            }

            DirectoryInfo folder = new DirectoryInfo(_srcPath);
            foreach(FileInfo file in folder.GetFiles())
            {
                if (".meta" == file.Extension)
                    continue;

#if UNITY_EDITOR_OSX
                // 排除meta文件
                if (".DS_Store" == file.Extension)
                    continue;
#endif

                file.CopyTo(string.Concat(_destPath, "/", file.Name), true);
            }

            foreach(DirectoryInfo dir in folder.GetDirectories())
            {
                string srcPath = dir.FullName;
                string destPath = string.Concat(_destPath, "/", dir.Name);
                _CopyDirectory(srcPath, destPath);
            }
        }
        catch
        {
            Debug.Log("Copy Path Error!!");
        }
    }


    // 1.处理所有lua脚本
    // [MenuItem("AssetsBundle/Procces Lua")]
    static void PreProccessLuaScript()
    {
        foreach (string strPath in strSrciptsPaths)
        {
            string strScriptPath = string.Concat(strSrcPath, "/", strPath);
            _ConvertLuaToTxt(strScriptPath);
        }
    }

    private static void _ConvertLuaToTxt(string _srcPath)
    {
        // 判断该目录是否存在
        string strExportPath = _srcPath;                    // _srcPath.Replace(strUndisposed, strTemp);
        if (!Directory.Exists(strExportPath))
            Directory.CreateDirectory(strExportPath);       // 在工程下创建Temp目录

        DirectoryInfo luaFolder = new DirectoryInfo(_srcPath);
        foreach (FileInfo fileInfo in luaFolder.GetFiles("*.lua"))
        {
            string dest = strExportPath + "/" + fileInfo.Name.Replace(".lua", ".txt");
            dest = dest.Replace(@"\", "/");
            fileInfo.CopyTo(dest, true);
            Debug.LogFormat("Export file to {0}", strExportPath);
        }

        foreach (FileInfo fileInfo in luaFolder.GetFiles("*.sproto"))
        {
            string dest = strExportPath + "/" + fileInfo.Name.Replace(".sproto", ".txt");
            dest = dest.Replace(@"\", "/");
            fileInfo.CopyTo(dest, true);
            Debug.LogFormat("Export file to {0}", strExportPath);
        }

        foreach (DirectoryInfo dir in luaFolder.GetDirectories())
        {
            _ConvertLuaToTxt(dir.FullName);
        }
    }


    // 2.设置资源的标签
    // [MenuItem("AssetsBundle/Set Label")]
    static void SetAssetsBundlesLabel()
    {
        AssetDatabase.Refresh();

        foreach(string strPath in strPackPaths)
        {
            string path = string.Concat(strSrcPath, "/", strPath);
            _SetAssetsBundlesLabel(path);
        }

        //_SetAssetsBundlesLabel(strTempPath, strTempPath);
    }

    public static void clearAssetBundlesName()
    {
        //获取所有的AssetBundle名称  
        string[] abNames = AssetDatabase.GetAllAssetBundleNames();

        //强制删除所有AssetBundle名称  
        for (int i = 0; i < abNames.Length; i++)
        {
            AssetDatabase.RemoveAssetBundleName(abNames[i], true);
        }
    }

    private static void _SetAssetsBundlesLabel(string _srcPath)
    {
        // string bundleName = _srcPath.Substring(_srcPath.LastIndexOf(strUndisposed));
        // bundleName = bundleName.Replace("/", "_");

        DirectoryInfo folder = new DirectoryInfo(_srcPath);
        foreach(FileInfo file in folder.GetFiles("*.*", SearchOption.AllDirectories))
        {
            _SetAssetBundleLabel(file);
        }
    }


    private static void _SetAssetBundleLabel(FileInfo file)
    {
        // 排除meta文件
        if (".meta" == file.Extension)
            return;

#if UNITY_EDITOR_OSX
        // 排除DS_Store文件
        if (".DS_Store" == file.Extension)
            return;
#endif

        string assetPath = file.FullName.Substring(file.FullName.IndexOf("Assets"));
        assetPath = assetPath.Replace(@"\", "/");
        AssetImporter importer = AssetImporter.GetAtPath(assetPath);

        int lastIdx = file.DirectoryName.LastIndexOf(strSrcPath);
        if (file.DirectoryName.Length == lastIdx)
            return;
        string bundleName = file.DirectoryName.Substring(lastIdx + strSrcPath.Length + 1);
        bundleName = bundleName.Replace(@"\", "/").Replace(@"/", "_");
        importer.assetBundleName = bundleName;
        if (".unity" == file.Extension)
            importer.assetBundleVariant = "u3d";
        else
            importer.assetBundleVariant = "pkg";
    }


    // 生成MD5
    // [MenuItem("AssetsBundle/Generate VersionInfo")]
    static void GenerateVersionInfo()
    {
        _GenerateVersionInfo(_GetProductBuildTarget(Application.platform));
    }


    private static void _GenerateVersionInfo(BuildTarget platform)
    {
        string genPath = _GetGeneratePath(platform);
        CSharpLuaTable ltVersion = new CSharpLuaTable();
        _GenerateVersionInfo(genPath, ref ltVersion);
        _GenerateMD5(genPath, ref ltVersion);
        _GenerateHash(genPath, ref ltVersion);
        _GenerateCRC(genPath, ref ltVersion);
        ltVersion.WriteToFileEx(string.Concat(genPath, "/", strVersionName), "VersionInfo");
    }


    private static void _GenerateVersionInfo(string _dir, ref CSharpLuaTable _root)
    {
        _root.SetField("version", GameVersion);
        _root.SetField("date", TimeHelper.GetCurrentDateWithYMD());
        _root.SetField("type", VersionType);
    }


    private static void _GenerateMD5(string _dir, ref CSharpLuaTable _root)
    {
        CSharpLuaTable ltMD5 = new CSharpLuaTable();
        CSharpLuaTable ltSize = new CSharpLuaTable();

        _root["md5"] = ltMD5;
        _root["size"] = ltSize;

        DirectoryInfo folder = new DirectoryInfo(_dir);
        foreach (FileInfo file in folder.GetFiles("*.*", SearchOption.AllDirectories))
        {
            // 排除meta文件
            if (".meta" == file.Extension)
                continue;

#if UNITY_EDITOR_OSX
            // 排除DS_Store文件
            if (".DS_Store" == file.Extension)
                continue;
#endif

            // 排除VersionInfo文件
            if (strVersionName == file.Name)
                continue;

            // 计算MD5值
            FileStream fs = file.OpenRead();    
            MD5 md5 = MD5.Create();
            byte[] fileMd5Bytes = md5.ComputeHash(fs);
            string fileMd5 = BitConverter.ToString(fileMd5Bytes).Replace("-", "").ToLower();
            ltMD5.SetField(file.Name, fileMd5);

            // 计算文件大小
            long size = file.Length;
            ltSize.SetField(file.Name, System.Convert.ToString(size));
        }
        return;
    }


    private static void _GenerateHash(string _dir, ref CSharpLuaTable _root)
    {
        CSharpLuaTable ltHash = new CSharpLuaTable();
        _root["hash"] = ltHash;

        AssetBundle.UnloadAllAssetBundles(true);
        AssetBundle ab = AssetBundle.LoadFromFile(string.Concat(_dir, "/", strRootAssetName));
        AssetBundleManifest manifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

        DirectoryInfo folder = new DirectoryInfo(_dir);
        foreach (FileInfo file in folder.GetFiles("*.*", SearchOption.AllDirectories))
        {
            // 排除meta文件
            if (".meta" == file.Extension)
                continue;
#if UNITY_EDITOR_OSX
            // 排除meta文件
            if (".DS_Store" == file.Extension)
                continue;
#endif
            // 排除VersionInfo文件
            if (strVersionName == file.Name)
                continue;

            // 获取hash值
            Hash128 hash = manifest.GetAssetBundleHash(file.Name);
            if (!hash.isValid)
                continue;

            ltHash.SetField(file.Name, hash.ToString());
        }
    }


    private static void _GenerateCRC(string _dir, ref CSharpLuaTable _root)
    {
        CSharpLuaTable ltCRC = new CSharpLuaTable();
        _root["crc"] = ltCRC;


        DirectoryInfo folder = new DirectoryInfo(_dir);
        foreach (FileInfo file in folder.GetFiles("*.*", SearchOption.AllDirectories))
        {
            // 排除meta文件
            if (".meta" == file.Extension)
                continue;

#if UNITY_EDITOR_OSX
            // 排除meta文件
            if (".DS_Store" == file.Extension)
                continue;
#endif

            // 排除VersionInfo文件
            if (strVersionName == file.Name)
                continue;

            // 获取CRC值
            uint crc = 0;
            BuildPipeline.GetCRCForAssetBundle(file.FullName, out crc);

            if (crc > 0)
                ltCRC.SetField(file.Name, System.Convert.ToString(crc));
        }
    }


    // mark 测试代码
    [MenuItem("AssetsBundle/TestLoad")]
    static void TestLoad()
    {
        AssetBundle.UnloadAllAssetBundles(true);


        string srcpath = string.Concat(Application.dataPath, "/Prefabs/UI/Login/Login.prefab");
        // abPath = "Prefabs/UI/Login/Login.prefab";
        string abname = _GenerateAssetBundleName(srcpath);


        string abpath = string.Concat(Application.dataPath, "/Prefabs/UI/Login/Login.prefab");

        string rootpath = string.Concat(Application.streamingAssetsPath, "/AssetBundles");
        AssetBundle assetBundle = AssetBundle.LoadFromFile(rootpath);
        AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

        string[] dependencies = manifest.GetAllDependencies("assetBundle");
        foreach (string dependency in dependencies)
        {
            AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, dependency));
        }


    }

    private static string _GenerateAssetBundleName(string _path)
    {
        string rt = _path.Replace(@"\\", "/").Replace(@"\", "/");
        string rp = "Assets/";
        int idx = rt.IndexOf(rp);
        if (idx > 0)
            rt = rt.Substring(idx + rp.Length);
        idx = rt.LastIndexOf("/");
        if (idx > 0)
            rt = rt.Substring(0, idx);

        rt = rt.Replace(@"/", "_");
        return rt;
    }
}
